'''
Created on 11/08/2010

@author: eh01
'''
import unittest
from services.config_service import ConfigService
from datastore.uri import Uri
from datastore.spaces import Spaces
from runtime.nodelogger import NodeLogger
from datastore.resource import Resource
from datastore.database import DataStore


class ConfigServiceTest(unittest.TestCase):


    def setUp(self):
        pass


    def tearDown(self):
        pass

    def create_test_definition_dict(self, int_seed):
        # int_seed is just an integer to make the strings unique for different seeds
        definition = {}
        definition[ConfigService.DC_TITLE] = 'This is the resource type title %d' % int_seed
        definition[ConfigService.DC_DESCRIPTION] = 'This is the resource type description %d' % int_seed
        definition[ConfigService.AUTOLOAD_PREFIX] = Uri('dummy/autoload/prefix/%d' % int_seed)
        definition[ConfigService.TEMPLATE] = Uri('dummy/template/%d' % int_seed)
        definition[ConfigService.REFERENCE] = 'This is the resource type reference %d' % int_seed
        definition[ConfigService.TERMTYPE] = 'This is the resource type term type %d' % int_seed
        definition[ConfigService.VERSION] = 'This is the resource type version %d' % int_seed
        definition[ConfigService.NOTE] = 'This is the resource type note %d' % int_seed
        definition[ConfigService.METADATA] = {ConfigService.DC_TITLE:'Metadata Title %d' % int_seed,
                                              ConfigService.DC_DESCRIPTION:'Metdata Description %d' % int_seed}
        definition[ConfigService.CONTENT] = {'a':'Value A %d' % int_seed,
                                             'b':'Value B %d' % int_seed}
        return definition

    def compare_service_and_service_definition_res(self, service, service_def_res):
        self.assertEqual(Uri('term:base/runtime/service'), service_def_res.get_type())
        self.assertEqual(service[ConfigService.DC_TITLE], service_def_res.get_metadata(Uri(ConfigService.DC_TITLE)))
        self.assertEqual(service[ConfigService.DC_DESCRIPTION], service_def_res.get_metadata(Uri(ConfigService.DC_DESCRIPTION)))
        self.assertEqual(service[ConfigService.CLASSNAME], service_def_res.get_var(Uri(ConfigService.CLASSNAME)))
        self.assertEqual(service[ConfigService.OPTIONS], service_def_res.get_var(Uri(ConfigService.OPTIONS)))
        self.assertEqual(service[ConfigService.API], service_def_res.get_var(Uri(ConfigService.API)))    
                
    def create_test_service_dict(self, int_seed):
        service = {}
        service[ConfigService.DC_TITLE] = 'This is the service title %d' % int_seed
        service[ConfigService.DC_DESCRIPTION] = 'This is the service description %d' % int_seed
        service[ConfigService.CLASSNAME] = 'com.abc.www.ABC%d' % int_seed
        service[ConfigService.OPTIONS] = {'option1': 'optionA %d' % int_seed}
        service[ConfigService.API] = {'api_name':{'uri':Uri('api/name/uri/%d' % int_seed)}}
        return service
            
    def compare_definition_and_definition_res(self, definition, definition_res):
        self.assertEqual(Uri('term:base/runtime/definition'), definition_res.get_type())
        self.assertEqual(definition[ConfigService.DC_TITLE], definition_res.get_metadata(Uri(ConfigService.DC_TITLE)))
        self.assertEqual(definition[ConfigService.DC_DESCRIPTION], definition_res.get_metadata(Uri(ConfigService.DC_DESCRIPTION)))
        self.assertEqual(definition[ConfigService.AUTOLOAD_PREFIX], definition_res.get_var(Uri(ConfigService.AUTOLOAD_PREFIX)))
        self.assertEqual(definition[ConfigService.TEMPLATE], definition_res.get_var(Uri(ConfigService.TEMPLATE)))
        self.assertEqual(definition[ConfigService.REFERENCE], definition_res.get_var(Uri(ConfigService.REFERENCE)))
        self.assertEqual(definition[ConfigService.TERMTYPE], definition_res.get_var(Uri(ConfigService.TERMTYPE)))
        self.assertEqual(definition[ConfigService.VERSION], definition_res.get_var(Uri(ConfigService.VERSION)))
        self.assertEqual(definition[ConfigService.NOTE], definition_res.get_var(Uri(ConfigService.NOTE)))
        self.assertEqual(definition[ConfigService.METADATA], definition_res.get_var(Uri(ConfigService.METADATA)))
        self.assertEqual(definition[ConfigService.CONTENT], definition_res.get_var(Uri(ConfigService.CONTENT)))
    
    def create_basic_config_res(self, sequence_number):
        config_res = Resource(type="term:base/runtime/configuration")
        config_res.set_metadata(Uri(ConfigService.DC_TITLE), "Test Config %d" % sequence_number)
        config_res.set_metadata(Uri('term:base/build/autoload_suffix'), '/autoload/suffix/%d' % sequence_number)
        config_res.sequence = sequence_number
        return config_res
            
    def test_create_definition_rs(self):
        # test all possible fields
        definition = self.create_test_definition_dict(1)
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        definition_res = config_service.create_definition_rs(definition)
        self.compare_definition_and_definition_res(definition, definition_res)
        
    def test_create_service_definition_rs(self):
        # test all possible fields
        service = {}
        service[ConfigService.DC_TITLE] = 'This is the service title'
        service[ConfigService.DC_DESCRIPTION] = 'This is the service description'
        service[ConfigService.CLASSNAME] = 'com.abc.www.ABC'
        service[ConfigService.OPTIONS] = {'option1': 'optionA'}
        service[ConfigService.API] = {'api_name':{'uri':Uri('api/name/uri')}}
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        service_def_res = config_service.create_service_definition_rs(service)
        self.assertEqual(Uri('term:base/runtime/service'), service_def_res.get_type())
        self.assertEqual(service[ConfigService.DC_TITLE], service_def_res.get_metadata(Uri(ConfigService.DC_TITLE)))
        self.assertEqual(service[ConfigService.DC_DESCRIPTION], service_def_res.get_metadata(Uri(ConfigService.DC_DESCRIPTION)))
        self.assertEqual(service[ConfigService.CLASSNAME], service_def_res.get_var(Uri(ConfigService.CLASSNAME)))
        self.assertEqual(service[ConfigService.OPTIONS], service_def_res.get_var(Uri(ConfigService.OPTIONS)))
        self.assertEqual(service[ConfigService.API], service_def_res.get_var(Uri(ConfigService.API)))    
    
    def test_process_bindings(self):
        config_res = Resource(type="term:base/runtime/configuration")        
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        # test it doesn't fail when the config file has no bindings
        config_service.process_bindings(config_res)
        self.assertEqual({}, spaces.resource)

        bindings = {'a':{ConfigService.BIND:Uri('bind/uri/a/')},
                    'b':{ConfigService.BIND:Uri('bind/uri/b/')}}
        config_res.set_var(ConfigService.BINDINGS, bindings)
        config_service.process_bindings(config_res)        
        for key, value in bindings.items():
            self.assertEqual(value[ConfigService.BIND], Uri.namespaces[key])
    
    def test_process_definitions(self):
        config_res = Resource(type="term:base/runtime/configuration")
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        # test it doesn't fail when the config file has no definitions   
        definition_ress = config_service.process_definitions(config_res)
        self.assertEqual({}, spaces.resource)
        self.assertEqual({}, definition_ress)
        
        config_res.definitions = {}
        uri_string_key_1 = "a/b/c"
        uri_string_key_2 = "d/e/f"
        uri_string_key_3 = "g/h/i"
        definition_1 = self.create_test_definition_dict(1)
        definition_2 = self.create_test_definition_dict(2)
        definition_3 = self.create_test_definition_dict(3)
        config_res.definitions[uri_string_key_1] = definition_1
        config_res.definitions[uri_string_key_2] = definition_2
        config_res.definitions[uri_string_key_3] = definition_3
        definition_ress_dict = config_service.process_definitions(config_res)
        # check in dict return
        self.compare_definition_and_definition_res(definition_1, definition_ress_dict[Uri(uri_string_key_1)])
        self.compare_definition_and_definition_res(definition_2, definition_ress_dict[Uri(uri_string_key_2)])
        self.compare_definition_and_definition_res(definition_3, definition_ress_dict[Uri(uri_string_key_3)])
        # check in spaces
        self.compare_definition_and_definition_res(definition_1, spaces.get(Uri(uri_string_key_1)))
        self.compare_definition_and_definition_res(definition_2, spaces.get(Uri(uri_string_key_2)))
        self.compare_definition_and_definition_res(definition_3, spaces.get(Uri(uri_string_key_3)))
        
    def test_process_services(self):
        config_res = Resource(type="term:base/runtime/configuration")
        config_res.set_metadata(Uri(ConfigService.DC_TITLE), "Test Config")
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        # test it doesn't fail when the config file has no services
        config_service.process_services(config_res, Spaces.ACTION_PUT)
        self.assertEqual({}, spaces.resource)
        
        # test service
        service_uri_string = 'a/b/c'
        service = self.create_test_service_dict(1)
        services = {service_uri_string:service}
        config_res.services = services
        config_service.process_services(config_res, Spaces.ACTION_PUT)
        self.compare_service_and_service_definition_res(service, spaces.get(Uri('internal:base/services-list/') + Uri(service_uri_string)))
        
        # test 2 services
        service_uri_string_1 = 'a/b/c'
        service_uri_string_2 = 'd/e/f'
        service_1 = self.create_test_service_dict(1)
        service_2 = self.create_test_service_dict(2)
        services = {service_uri_string_1:service_1,service_uri_string_2:service_2}
        config_res.services = services
        try:
            config_service.process_services(config_res, Spaces.ACTION_PUT)
            self.fail("Should fail if we have more than one service in the config file.")
        except:
            self.assertTrue(True)
                    
    def test_autoload_resources_defined_in_config(self):
        suffix_uri = Uri('term:base/build/autoload_suffix')
        # create resoure types that will be autoloaded in spaces except for definition_2
        definition_1 = self.create_test_definition_dict(1)
        definition_2 = self.create_test_definition_dict(2)
        definition_3 = self.create_test_definition_dict(3)
        def_1_prefix = Uri('1/2/3')
        def_3_prefix = Uri('7/8/9')
        definition_1[ConfigService.AUTOLOAD_PREFIX] = Uri('1/2/3')
        # make one of the resource definitions without autoload
        del definition_2[ConfigService.AUTOLOAD_PREFIX]
        definition_3[ConfigService.AUTOLOAD_PREFIX] = Uri('7/8/9')
        res_type_def_uri_1 = "a/b/c"
        res_type_def_uri_2 = "d/e/f"
        res_type_def_uri_3 = "g/h/i"
        
        # create resources that will autoload in spaces
        rs1a = Resource(type=Uri(res_type_def_uri_1))
        rs1b = Resource(type=Uri(res_type_def_uri_1))
        rs2a = Resource(type=Uri(res_type_def_uri_2))
        rs3a = Resource(type=Uri(res_type_def_uri_3))   
        rs1a_suffix_uri = Uri("x/y/z")
        rs1b_suffix_uri = Uri("u/v/w")
        rs3a_suffix_uri = Uri("r/s/t")
        rs1a.set_metadata(suffix_uri, rs1a_suffix_uri)
        rs1b.set_metadata(suffix_uri, rs1b_suffix_uri)
        rs3a.set_metadata(suffix_uri, rs3a_suffix_uri)
        
        config_res = Resource(type="term:base/runtime/configuration")
        config_res.definitions = {}
        config_res.definitions[res_type_def_uri_1] = definition_1
        config_res.definitions[res_type_def_uri_2] = definition_2
        config_res.definitions[res_type_def_uri_3] = definition_3
        
        ds = DataStore(":memory:")
        
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        config_service.datastore = ds
        
        # check case when there are no definitions in the config file
        # no need to assert anything. Just make sure it doesn't fall over 
        # when passed a empty list
        config_service.autoload_resources_defined_in_config([])
        
        # check case when there are no resources to autoload in datastore
        definition_ress_dict = config_service.process_definitions(config_res)
        config_service.autoload_resources_defined_in_config(definition_ress_dict)
        
        # check resources are not loaded into spaces        
        self.assertEqual(None, spaces.get(def_1_prefix + rs1a_suffix_uri))
        self.assertEqual(None, spaces.get(def_1_prefix + rs1b_suffix_uri))
        self.assertEqual(None, spaces.get(def_3_prefix + rs3a_suffix_uri))
        
        # check case when there are resources to autoload in datastore
        ds.put([rs1a, rs1b, rs2a, rs3a])
        config_service.autoload_resources_defined_in_config(definition_ress_dict)        
        
        # check the resources are in spaces except for rs2a
        self.assertTrue(rs1a in spaces.resource.values())
        self.assertTrue(rs1b in spaces.resource.values())
        self.assertFalse(rs2a in spaces.resource.values())
        self.assertTrue(rs3a in spaces.resource.values())
        
        # check they are loaded to the correct uri in spaces        
        self.assertEqual(rs1a, spaces.get(def_1_prefix + rs1a_suffix_uri))
        self.assertEqual(rs1b, spaces.get(def_1_prefix + rs1b_suffix_uri))
        self.assertEqual(rs3a, spaces.get(def_3_prefix + rs3a_suffix_uri))
        
        # make a new rs1a in datastore and auto load again to make sure the old resource
        # in spaces is replaced
        ds.delete([rs1a])
        new_rs1a = Resource(type=Uri(res_type_def_uri_1))
        new_rs1a.set_metadata(suffix_uri, rs1a_suffix_uri)
        # attribute to distinguish between the old and new rs1a
        new_rs1a.newattr = 'hello'
        ds.put([new_rs1a])
        
        config_service.autoload_resources_defined_in_config(definition_ress_dict)

        # check all the old resources are still there
        self.assertTrue(rs1b in spaces.resource.values())
        self.assertFalse(rs2a in spaces.resource.values())
        self.assertTrue(rs3a in spaces.resource.values())
    
        # check new_rs1a is there, but rs1a is not    
        self.assertFalse(rs1a in spaces.resource.values())
        self.assertTrue(new_rs1a in spaces.resource.values())
        
        # check all the old resources are still in spaces at the same location        
        self.assertEqual(rs1b, spaces.get(def_1_prefix + rs1b_suffix_uri))
        self.assertEqual(rs3a, spaces.get(def_3_prefix + rs3a_suffix_uri))
        
        # check that new_rs1a replaced ns1a location in spaces
        self.assertEqual(new_rs1a, spaces.get(def_1_prefix + rs1a_suffix_uri))
        self.assertNotEqual(rs1a, spaces.get(def_1_prefix + rs1a_suffix_uri))        
        
    
    def test_cb_config_update(self):
        # Test with empty configuration
        config_res = Resource(type="term:base/runtime/configuration")
        config_res.set_metadata(Uri(ConfigService.DC_TITLE), "Test Config")
        ds = DataStore(":memory:")
        
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        config_service.datastore = ds
        
        # Test load config
        config_service.cb_config_update(Uri(), Spaces.ACTION_PUT, config_res)
        # This is should be None as it is not a updated config, it shouldn't 
        # be updated in the datastore, hence the id should be None in our test
        self.assertEqual(None, config_res.get_id())
        
        # Test load updated config
        config_service.cb_config_update(Uri(), Spaces.ACTION_UPDATE, config_res)
        # This is should be not None as it is a updated config. The updated config 
        # should be saved in the datastore. Hence the resource will be allocated 
        # an ID
        self.assertNotEqual(None, config_res.get_id())
        
    def test_sort_config_list_by_sequence_number(self):
        config_1 = self.create_basic_config_res(1)
        config_2 = self.create_basic_config_res(2)
        dup_config_2 = self.create_basic_config_res(2)
        config_3 = self.create_basic_config_res(3)
        ds = DataStore(":memory:")
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        config_service.datastore = ds
        sorted_config_list = config_service.sort_config_list_by_sequence_number([config_2,config_1,config_3])
        self.assertEqual([config_1, config_2, config_3], sorted_config_list)
        
        # test duplicate sequence number raises a exception
        self.assertRaises(Exception, config_service.sort_config_list_by_sequence_number, [config_2,config_1,dup_config_2,config_3])
        
    def test_load_configs(self):
        config_1 = self.create_basic_config_res(1)
        config_2 = self.create_basic_config_res(2)
        config_2.set_metadata(Uri('term:base/runtime/active'), True)
        config_3 = self.create_basic_config_res(3)
        ds = DataStore(":memory:")
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        config_service.instances = Uri('internal:base/config/')
        config_service.datastore = ds
        config_service.load_configs([config_1, config_2, config_3])
        config_uri_1 = config_service.instances + config_1.get_metadata(
                            Uri('term:base/build/autoload_suffix'))
        config_uri_2 = config_service.instances + config_2.get_metadata(
                            Uri('term:base/build/autoload_suffix'))
        config_uri_3 = config_service.instances + config_3.get_metadata(
                            Uri('term:base/build/autoload_suffix'))
        
        # check the configs have been loaded with configs marked as Active and
        # configs that don't have the active metadata
        self.assertEqual(config_1, spaces.get(config_uri_1))
        self.assertEqual(config_2, spaces.get(config_uri_2))
        self.assertEqual(config_3, spaces.get(config_uri_3))
        
        config_4 = self.create_basic_config_res(4)
        config_4.del_metadata(Uri('term:base/build/autoload_suffix'))
        try:
            config_service.load_configs([config_4])
            self.fail('Should have failed if the config didnt have an autoload suffix')
        except:
            self.assertTrue(True)
        
        config_5 = self.create_basic_config_res(5)
        config_5.set_metadata(Uri('term:base/runtime/active'), False)
        config_service.load_configs([config_5])
        config_uri_5 = config_service.instances + config_5.get_metadata(
                            Uri('term:base/build/autoload_suffix'))
        # check it hasn't been loaded as it is marked as NOT ACTIVE
        self.assertEqual(None, spaces.get(config_uri_5))
        
    def test_cb_start(self):
        ds = DataStore(":memory:")
        NodeLogger.load()
        spaces = Spaces()
        uri = Uri()
        config_service = ConfigService(spaces=spaces, uri=uri)
        config_service.datastore = ds
        # test no config files
        config_service.cb_start(None, None, None)
        self.assertEqual([(config_service.cb_config_update, ['put', 'update'])], spaces.watch[config_service.instances])
        
        # test with config files, just test it doesn't fall down.
        config_res = self.create_basic_config_res(1)
        ds.put(config_res)
        config_service.cb_start(None, None, None)
        
if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']
    unittest.main()